[Chapter Six][Previous]
[Next] [Art of
Assembly][Randall Hyde]
Art of Assembly: Chapter Six
- 6.6.4 - The Bit Operations
- 6.6.4.1 - TEST
- 6.6.4.2 - The Bit Test Instructions: BT,
BTS, BTR, and BTC
- 6.6.4.3 - Bit Scanning: BSF and BSR
- 6.6.5 - The "Set on Condition"
Instructions
- 6.7 - I/O Instructions
- 6.8 - String Instructions
6.6.4 The Bit Operations
Bit twiddling is one of those operations easier done in assembly language
than other languages. And no wonder. Most high-level languages shield you
from the machine representation of the underlying data types. Instructions
like and, or, xor, not,
and the shifts and rotates make it
possible to test, set, clear, invert, and align bit fields within strings
of bits. Even the C++ programming language, famous for its bit manipulation
operators, doesn't provide the bit manipulation capabilities of assembly
language.
The 80x86 family, particularly the 80386 and later processors, go much farther,
though. Besides the standard logical, shift, and rotate instructions, there
are instructions to test bits within an operand, to test and set, clear,
or invert specific bits in an operand, and to search for set bits. These
instructions are
test dest, source
bt source, index
btc source, index
btr source, index
bts source, index
bsf dest, source
bsr dest, source
The specific forms are
test reg, reg
test reg, mem
test mem, reg (*)
test reg, imm
test mem, imm
test eax/ax/al, imm
bt reg, reg (3)
bt mem, reg (3)
bt reg, imm (3)
bt mem, imm (3)
btc uses the same formats as bt. (3)
btr uses the same formats as bt. (3)
bts uses the same formats as bt. (3)
bsf reg, reg (3)
bsr reg, mem (3)
bsr uses the same formats as bsf. (3)
3- This instruction is only available on 80386 and later processors.
*- This is the same instruction as test reg,mem
Note that the bt, btc, btr, bts, bsf,
and bsr
require 16 or 32 bit operands.
The bit operations are useful when implementing (monochrome) bit mapped
graphic primitive functions and when implementing a set data type using
bit maps.
6.6.4.1 TEST
The test
instruction logically ands its two operands and sets
the flags but does not save the result. Test
and and
share the same relationship as cmp
and sub
. Typically,
you would use this instruction to see if a bit contains one. Consider the
following instruction:
test al, 1
This instruction logically ands al
with the value one. If bit
zero of al
contains a one, the result is non-zero and the 80x86
clears the zero flag. If bit zero of al
contains zero, then
the result is zero and the test
operation sets the zero flag.
You can test the zero flag after this instruction to decide whether al
contained zero or one in bit zero.
The test
instruction can also check to see if one or more bits
in a register or memory location are non-zero. Consider the following instruction:
test dx, 105h
This instruction logically ands dx
with the value 105h. This
will produce a non-zero result (and, therefore, clear the zero flag) if
at least one of bits zero, two, or eight contain a one. They must all be
zero to set the zero flag.
The test
instruction sets the flags identically to the and
instruction:
- It clears the carry flag.
- It clears the overflow flag.
- It sets the zero flag if the result is zero, they clear it otherwise.
- It copies the H.O. bit of the result into the sign flag.
- It sets the parity flag according to the parity (number of one bits)
in the L.O. byte of the result.
- It scrambles the auxiliary carry flag.
6.6.4.2 The Bit Test Instructions: BT, BTS, BTR, and BTC
On an 80386 or later processor, you can use the bt
instruction
(bit test) to test a single bit. Its second operand specifies the bit index
into the first operand. Bt
copies the addressed bit into the
carry flag. For example, the instruction
bt ax, 12
copies bit twelve of ax
into the carry flag.
The bt/bts/btr/btc
instructions only deal with 16 or 32 bit
operands. This is not a limitation of the instruction. After all, if you
want to test bit three of the al
register, you can just as
easily test bit three of the ax
register. On the other hand,
if the index is larger than the size of a register operand, the result is
undefined.
If the first operand is a memory location, the bt
instruction
tests the bit at the given offset in memory, regardless the value of the
index. For example, if bx
contains 65 then
bt TestMe, bx
will copy bit one of location TestMe+8
into the carry flag.
Once again, the size of the operand does not matter. For all intents and
purposes, the memory operand is a byte and you can test any bit after that
byte with an appropriate index. The actual bit bt
tests is
at bit position index mod 8 and at memory offset effective address + index/8.
The bts
, btr
, and btc
instructions
also copy the addressed bit into the carry flag. However, these instructions
also set, reset (clear), or complement (invert) the bit in the first operand
after copying it to the carry flag. This provides test and set, test and
clear, and test and invert operations necessary for some concurrent algorithms.
The bt, bts, btr, and btc instructions do not affect any flags other than
the carry flag.
6.6.4.3 Bit Scanning: BSF and BSR
The bsf
(Bit Scan Forward) and bsr
(Bit Scan Reverse)
instructions search for the first or last set bit in a 16 or 32 bit quantity.
The general form of these instructions is
bsf dest, source
bsr dest, source
Bsf
locates the first set bit in the source operand, searching
from bit zero though the H.O. bit. Bsr
locates the first set
bit searching from the H.O. bit down to the L.O. bit. If these instructions
locate a one, they clear the zero flag and store the bit index (0..31) into
the destination operand. If the source operand is zero, these instructions
set the zero flag and store an indeterminate value into the destination
operand.
To scan for the first bit containing zero (rather than one), make a copy
of the source operand and invert it (using not
), then execute
bsf
or bsr
on the inverted value. The zero flag
would be set after this operation if there were no zero bits in the original
source value, otherwise the destination operation will contain the position
of the first bit containing zero.
6.6.5 The "Set on Condition" Instructions
The set on condition (or setcc
) instructions set a single byte
operand (register or memory location) to zero or one depending on the values
in the flags register. The general formats for the setcc
instructions
are
setcc reg8
setcc mem8
Setcc
represents a mnemonic appearing in the following tables.
These instructions store a zero into the corresponding operand if the condition
is false, they store a one into the eight bit operand if the condition is
true.
SETcc Instructions That Test Flags
Instruction | Description | Condition | Comments |
---|
SETC | Set
if carry | Carry = 1 | Same as SETB, SETNAE |
SETNC | Set if no
carry | Carry = 0 | Same as SETNB, SETAE |
SETZ | Set if zero | Zero
= 1 | Same as SETE |
SETNZ | Set if not zero | Zero = 0 | Same
as SETNE |
SETS | Set if sign | Sign = 1 | - |
SETNS | Set
if no sign | Sign = 0 | - |
SETO | Set if overflow | Ovrflw=1 | - |
SETNO | Set if no overflow | Ovrflw=0 | - |
SETP | Set if
parity | Parity = 1 | Same as SETPE |
SETPE | Set if parity even | Parity
= 1 | Same as SETP |
SETNP | Set if no parity | Parity = 0 | Same
as SETPO |
SETPO | Set if parity odd | Parity = 0 | Same as SETNP |
The setcc
instructions above simply test the flags without
any other meaning attached to the operation. You could, for example, use
setc
to check the carry flag after a shift, rotate, bit test,
or arithmetic operation. Likewise, you could use setnz
instruction
after a test
instruction to check the result.
The cmp
instruction works synergistically with the setcc
instructions. Immediately after a cmp
operation the processor
flags provide information concerning the relative values of those operands.
They allow you to see if one operand is less than, equal to, greater than,
or any combination of these.
There are two groups of setcc
instructions that are very useful
after a cmp
operation. The first group deals with the result
of an unsigned comparison, the second group deals with the result of a signed
comparison.
SETcc Instructions for Unsigned Comparisons
Instruction | Description | Condition | Comments |
---|
SETA | Set if above (>) | Carry=0, Zero=0 | Same as SETNBE |
SETNBE | Set if not below or equal (not <=) | Carry=0, Zero=0 | Same as SETA |
SETAE | Set if above or equal (>=) | Carry = 0 | Same as SETNC, SETNB |
SETNB | Set if not below (not <) | Carry = 0 | Same as SETNC, SETAE |
SETB | Set if below (<) | Carry = 1 | Same as SETC, SETNAE |
SETNAE | Set if not above or equal (not >=) | Carry = 1 | Same as SETC, SETB |
SETBE | Set if below or equal (<=) | Carry = 1 or Zero = 1 | Same as SETNA |
SETNA | Set if not above (not >) | Carry = 1 or Zero = 1 | Same as SETBE |
SETE | Set if equal (=) | Zero = 1 | Same as SETZ |
SETNE | Set if not equal () | Zero = 0 | Same as SETNZ |
The corresponding table for signed comparisons is
SETcc Instructions for Signed Comparisons
Instruction | Description | Condition | Comments |
---|
SETG | Set if greater (>) | Sign = Ovrflw or Zero=0 | Same as SETNLE |
SETNLE | Set if not less than or equal (not <=) | Sign = Ovrflw or Zero=0
| Same as SETG |
SETGE | Set if greater than or equal (>=) | Sign = Ovrflw | Same as SETNL |
SETNL | Set if not less than (not <) | Sign = Ovrflw | Same as SETGE |
SETL | Set if less than (<) | Sign Ovrflw | Same as SETNGE |
SETNGE | Set if not greater or equal (not >=) | Sign Ovrflw
| Same as SETL |
SETLE | Set if less than or equal (<=) | Sign Ovrflw or
Zero = 1 | Same as SETNG |
SETNG | Set if not greater than (not >) | Sign Ovrflw or
Zero = 1 | Same as SETLE |
SETE | Set if equal (=) | Zero = 1 | Same as SETZ |
SETNE | Set if not equal () | Zero = 0 | Same as SETNZ |
The setcc
instructions are particularly valuable because they
can convert the result of a comparison to a boolean value (true/false or
0/1). This is especially important when translating statements from a high
level language like Pascal or C++ into assembly language. The following
example shows how to use these instructions in this manner:
; Bool := A <= B
mov ax, A ;Assume A and B are signed integers.
cmp ax, B
setle Bool ;Bool needs to be a byte variable.
Since the setcc
instructions always produce zero or one, you
can use the results with the logical and
and or
instructions to compute complex boolean values:
; Bool := ((A <= B) and (D = E)) or (F <> G)
mov ax, A
cmp ax, B
setle bl
mov ax, D
cmp ax, E
sete bh
and bl, bh
mov ax, F
cmp ax, G
setne bh
or bl, bh
mov Bool, bh
For more examples, see Chapter Nine.
The setcc
instructions always produce an eight bit result since
a byte is the smallest operand the 80x86 will operate on. However, you can
easily use the shift and rotate instructions to pack eight boolean values
in a single byte. The following instructions compare eight different values
with zero and copy the "zero flag" from each comparison into corresponding
bits of al
:
cmp Val7, 0
setne al ;Put first value in bit #0
cmp Val6, 0 ;Test the value for bit #6
setne ah ;Copy zero flag into ah register.
shr ah, 1 ;Copy zero flag into carry.
rcl al, 1 ;Shift carry into result byte.
cmp Val5, 0 ;Test the value for bit #5
setne ah
shr ah, 1
rcl al, 1
cmp Val4, 0 ;Test the value for bit #4
setne ah
shr ah, 1
rcl al, 1
cmp Val3, 0 ;Test the value for bit #3
setne ah
shr ah, 1
rcl al, 1
cmp Val2, 0 ;Test the value for bit #2
setne ah
shr ah, 1
rcl al, 1
cmp Val1, 0 ;Test the value for bit #1
setne ah
shr ah, 1
rcl al, 1
cmp Val0, 0 ;Test the value for bit #0
setne ah
shr ah, 1
rcl al, 1
; Now AL contains the zero flags from the eight comparisons.
6.7 I/O Instructions
The 80x86 supports two I/O instructions: in
and out
.
They take the forms:
in eax/ax/al, port
in eax/ax/al, dx
out port, eax/ax/al
out dx, eax/ax/al
port
is a value between 0 and 255.
The 80x86 supports up to 65,536 different I/O ports (requiring a 16 bit
I/O address). The port
value above, however, is a single byte
value. Therefore, you can only directly address the first 256 I/O ports
in the 80x86's I/O address space. To address all 65,536 different I/O ports,
you must load the address of the desired port (assuming it's above 255)
into the dx
register and access the port indirectly. The in
instruction reads the data at the specified I/O port and copies it into
the accumulator. The out
instruction writes the value in the
accumulator to the specified I/O port.
Please realize that there is nothing magical about the 80x86's in
and out
instructions. They're simply another form of the mov
instruction that accesses a different memory space (the I/O address space)
rather than the 80x86's normal 1 Mbyte memory address space.
The in
and out
instructions do not affect any
80x86 flags.
Examples of the 80x86 I/O instructions:
in al, 60h ;Read keyboard port
mov dx, 378h ;Point at LPT1: data port
in al, dx ;Read data from printer port.
inc ax ;Bump the ASCII code by one.
out dx, al ;Write data in AL to printer port.
6.8 String Instructions
The 80x86 supports twelve string instructions:
movs
(move string)
lods
(load string element into the accumulator)
stos
(store accumulator into string element)
scas
(Scan string and check for match against the value
in the accumulator)
cmps
(compare two strings).
ins
(input a string from an I/O port)
outs
(output a string to an I/O port
rep
(repeat a string operation)
repz
(repeat while zero)
repe
(repeat while equal)
repnz
(repeat while not zero)
repne
(repeat while not equal)
You can use the movs, stos, scas, cmps, ins
and outs
instructions to manipulate a single element (byte, word, or double word)
in a string, or to process an entire string. Generally, you would only use
the lods
instruction to manipulate a single item at a time.
These instructions can operate on strings of bytes, words, or double words.
To specify the object size, simply append a b, w, or d to the end of the
instruction's mnemonic, i.e., lodsb, movsw, cmpsd,
etc. Of
course, the double word forms are only available on 80386 and later processors.
The movs
and cmps
instructions assume that ds:si
contains the segmented address of a source string and that es:di
contains the segmented address of a destination string. The lods
instruction assumes that ds:si
points at a source string, the
accumulator (al/ax/eax
) is the destination location. The scas
and stos
instructions assume that es:di
points
at a destination string and the accumulator contains the source value.
The movs
instruction moves one string element (byte, word,
or dword) from memory location ds:si
to es:di
.
After moving the data, the instruction increments or decrements si
and di
by one, two, or four if processing bytes, words, or
dwords, respectively. The CPU increments these registers if the direction
flag is clear, the CPU decrements them if the direction flag is set.
The movs
instruction can move blocks of data around in memory.
You can use it to move strings, arrays, and other multi-byte data structures.
movs{b,w,d}: es:[di] := ds:[si]
if direction_flag = 0 then
si := si + size;
di := di + size;
else
si := si - size;
di := di - size;
endif;
Note: size
is one for bytes, two for words, and four for dwords.
The cmps
instruction compares the byte, word, or dword at location
ds:si
to es:di
and sets the processor flags accordingly.
After the comparison, cmps
increments or decrements si
and di
by one, two, or four depending on the size of the instruction
and the status of the direction flag in the flags register.
cmps{b,w,d}: cmp ds:[si], es:[di]
if direction_flag = 0 then
si := si + size;
di := di + size;
else
si := si - size;
di := di - size;
endif;
The lods
instruction moves the byte, word, or dword at ds:si
into the al
, ax
, or eax
register.
It then increments or decrements the si
register by one, two,
or four depending on the instruction size and the value of the direction
flag. The lods
instruction is useful for fetching a sequence
of bytes, words, or double words from an array, performing some operation(s)
on those values and then processing the next element from the string.
lods{b,w,d}: eax/ax/al := ds:[si]
if direction_flag = 0 then
si := si + size;
else
si := si - size;
endif;
The stos
instruction stores al
, ax
,
or eax
at the address specified by es:di
. Again,
di
is incremented or decremented according to the size of the
instruction and the value of the direction flag. The stos
instruction
has several uses. Paired with the lods
instruction above, you
can load (via lods
), manipulate, and store string elements.
By itself, the stos
instruction can quickly store a single
value throughout a multi-byte data structure.
stos{b,w,d}: es:[di] := eax/ax/al
if direction_flag = 0 then
di := di + size;
else
di := di - size;
endif;
The scas
instruction compares al, ax
or eax
against the value at location es:di
and then adjusts di
accordingly. This instruction sets the flags in the processor status register
just like the cmp
and cmps
instructions. The scas
instruction is great for searching for a particular value throughout some
multi-byte data structure.
scas{b,w,d}: cmp eax/ax/al, es:[di]
if direction_flag = 0 then
di := di + size;
else
di := di - size;
endif;
The ins
instruction inputs a byte, word, or double word from
the I/O port specified in the dx
register. It then stores the
input value at memory location es:di
and increments or decrements
di
appropriately. This instruction is available only on 80286
and later processors.
ins{b,w,d}: es:[di] := port(dx)
if direction_flag = 0 then
di := di + size;
else
di := di - size;
endif;
The outs
instruction fetches the byte, word, or double word
at address ds:si
, increments or decrements si
accordingly, and then outputs the value to the port specified in the dx
register.
outs{b,w,d}: port(dx) := ds:[si]
if direction_flag = 0 then
si := si + size;
else
si := si - size;
endif;
As explained here, the string instructions are useful, but it gets even
better! When combined with the rep, repz, repe, repnz,
and
repne
prefixes, a single string instruction can process an
entire string. For more information on these prefixes see the chapter on
strings.
- 6.6.4 - The Bit Operations
- 6.6.4.1 - TEST
- 6.6.4.2 - The Bit Test Instructions: BT,
BTS, BTR, and BTC
- 6.6.4.3 - Bit Scanning: BSF and BSR
- 6.6.5 - The "Set on Condition"
Instructions
- 6.7 - I/O Instructions
- 6.8 - String Instructions
Art of Assembly: Chapter Six - 26 SEP 1996
[Chapter Six][Previous]
[Next] [Art of
Assembly][Randall Hyde]